package com.supermap.sdc2023;

import com.supermap.services.security.OAuth2Client;
import com.supermap.services.security.OAuthUserInfo;
import org.apache.commons.io.IOUtils;
import org.json.JSONObject;

import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.Locale;

public class OAuth2ClientDemo implements OAuth2Client {
    private static final String REDIRECT_URI_TEMPLATE = "http://sdc2023demo:10580/realms/master/protocol/openid-connect/auth?client_id=%s&redirect_uri=%s&state=%s&response_mode=query&response_type=code&scope=sdc2023";
    private static final String TOKEN_ENDPOINT = "http://sdc2023demo:10580/realms/master/protocol/openid-connect/token";
    public String getRedirectURI(String clientID, String state, String redirectUri) {
        return String.format(Locale.ENGLISH, REDIRECT_URI_TEMPLATE, clientID, URLEncoder.encode(redirectUri, StandardCharsets.UTF_8), state);
    }

    public String getAccesstoken(String clientID, String clientSecret, String code, String redirectUri) throws IOException {
        URL url = new URL(TOKEN_ENDPOINT);
        HttpCall<String> then = httpConn -> {
            httpConn.setDoOutput(true);
            httpConn.setRequestMethod("POST");
            httpConn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
            try(OutputStream os  = httpConn.getOutputStream()) {
                String bodyTemplate = "client_id=%s&client_secret=%s&code=%s&redirect_uri=%s&grant_type=authorization_code";
                String body = String.format(Locale.ENGLISH, bodyTemplate, clientID, clientSecret, code, redirectUri);
                os.write(body.getBytes(StandardCharsets.UTF_8));
            }
            return readAsJson(httpConn).getString("access_token");
        };
        return openThen(url, then);
    }

    private static JSONObject getUserInfo(String accessToken) throws MalformedURLException {
        HttpCall<JSONObject> then = httpConn -> {
            httpConn.setRequestProperty("Authorization", "Bearer " +  accessToken);
            httpConn.setRequestProperty("Accept", "application/json");
            return readAsJson(httpConn);
        };
        return openThen(new URL("http://sdc2023demo:8080/"), then);
    }

    public String getUserID(String accesstoken) throws IOException {
        return getUserInfo(accesstoken).getString("id");
    }

    public OAuthUserInfo getUserInfo(String token, String clientID, String userID) throws IOException {
        OAuthUserInfo ret = new OAuthUserInfo();
        JSONObject userInfo = getUserInfo(token);
        ret.nickName = userInfo.getString("userName");
        return ret;
    }

    private static HttpURLConnection open(URL url) throws IOException {
        Proxy httpProxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1",  10380) );
        return (HttpURLConnection) url.openConnection(httpProxy);
    }

    private static <T> T openThen(URL url, HttpCall<T> call) {
        HttpURLConnection httpURLConnection = null;
        try {
            httpURLConnection = open(url);
            return call.call(httpURLConnection);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            httpURLConnection.disconnect();
        }
    }

    @FunctionalInterface
    private static interface HttpCall<T> {
        T call(HttpURLConnection httpURLConnection) throws Exception;
    }

    private static JSONObject readAsJson(HttpURLConnection httpURLConnection) throws IOException {
        try(InputStream is = httpURLConnection.getInputStream()) {
            return  new JSONObject(IOUtils.toString(is, StandardCharsets.UTF_8));
        }
    }
}
